﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;

namespace TrackType
{

    /// <summary>
    /// Base class for all TrackType.NET web requests
    /// </summary>
    public abstract class TrackTypeRequestBase
    {
        WebProxy Proxy;

        /// <summary>
        /// Creates new instance using givin WebProxy
        /// </summary>
        /// <param name="Proxy">WebProxy which will be used to 
        /// communicate with tracktype site. Can be null</param>
        public TrackTypeRequestBase(WebProxy Proxy)
        {
            this.Proxy = Proxy;
        }

        /// <summary>
        /// Executes request and returns response
        /// </summary>
        /// <returns></returns>
        public abstract TrackTypeResponseBase Execute();

        /// <summary>
        /// Adds HelloBanner and ProtoBanner to base command str
        /// </summary>
        /// <param name="baseStr">Command string without any banners</param>
        /// <returns>Command string with HelloBanner and ProtoBanner at the end</returns>
        protected string AddProtocolCharacters(string baseStr)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(baseStr);
            sb.Append("&");
            sb.Append(TrackTypeConfig.HelloBanner);
            sb.Append("&");
            sb.Append(TrackTypeConfig.ProtoBanner);
            return sb.ToString();
        }

        /// <summary>
        /// Formats string according to protocol (currently returns
        /// baseStr converted to lower case)
        /// </summary>
        /// <param name="str">Base string</param>
        /// <returns>String that is formated according to procotol </returns>
        protected string FormatLine(string str)
        {
            return str.ToLowerInvariant();
        }

        /// <summary>
        /// Executes command_text in terms of requesting http-answer from remote server
        /// </summary>
        /// <param name="command_text">Command text to execute</param>
        /// <returns>Response lines</returns>
        protected IList<string> GetWebResponse(string command_text)
        {
            IList<string> result = new List<string>();

            HttpWebResponse response = null;
            StreamReader reader = null;
            try
            {
                string url = TrackTypeConfig.Url;
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Proxy = Proxy;
                request.ContentType = "text/plain";
                request.Method = "POST";
                request.Timeout = TrackTypeConfig.RequestTimeout;

                byte[] cmd_bytes = Encoding.UTF8.GetBytes(command_text);

                Stream stream = request.GetRequestStream();

                stream.Write(cmd_bytes, 0, cmd_bytes.Length);
                stream.Close();

                response = (HttpWebResponse)request.GetResponse();
                //put the results into a StreamReader
                reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8);
                // add each line to the StringCollection until we get the terminator
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    if (line.StartsWith("."))
                        break;
                    else
                        result.Add(line);
                }
            }
            catch { }
            finally
            {
                if (response != null)
                    response.Close();
                if (reader != null)
                    reader.Close();
            }

            return result;
        }
    }

    /// <summary>
    /// Base class for all TrackType.NET web responses
    /// </summary>
    public abstract class TrackTypeResponseBase
    {
        protected const string found_exact_match = "200";
        protected const string no_match = "202";
        protected const string found_inexact_matches = "211";

        protected string _code;
        protected string _message;

        /// <summary>
        /// Gets response code recieved from tracktype site
        /// </summary>
        public string Code
        {
            get
            {
                return _code;
            }
        }
        /// <summary>
        /// Gets response message recieved from tracktype site
        /// </summary>
        public string Message
        {
            get
            {
                return _message;
            }
        }

        private TrackTypeResponseBase() { }
        /// <summary>
        /// Creates new instance using list of text lines recieved from tracktype site.
        /// Don't create TrackTypeResponseBase class instance directly. Use appropriative
        /// requests and call their Execute() methods instead
        /// </summary>
        /// <param name="lines"></param>
        protected TrackTypeResponseBase(IList<string> lines)
        {
            if (lines != null && lines.Count > 0)
            {
                string headerLine = lines[0];
                if (!ParseResponseHeader(headerLine))
                {
                    throw new InvalidOperationException();
                }
            }
            else
                throw new ArgumentException();
        }

        /// <summary>
        /// Attempts to parse response header in order to fill Code and Message properties
        /// </summary>
        /// <param name="headerStr">Header string</param>
        /// <returns>True if succeded, false otherwise</returns>
        protected bool ParseResponseHeader(string headerStr)
        {
            bool result = false;

            if (!String.IsNullOrEmpty(headerStr))
            {
                string[] words = headerStr.Split(' ');

                if (words != null && words.Length > 0)
                {
                    try
                    {
                        _code = words[0];

                        for (int i = 1; i < words.Length; ++i)
                            _message += words[i] + ' ';

                        _message = _message.TrimEnd(' ');

                        result = true;
                    }
                    catch { }
                }
            }

            return result;
        }
    }
}
